//
//  QueueTaskViewController.m
//  ThreadDemo
//
//  Created by 苏沫离 on 2018/7/12.
//  Copyright © 2018年 苏沫离. All rights reserved.
//
#define CellIdentifer @"UITableViewCell"

#import "QueueTaskViewController.h"

@interface QueueTaskViewController ()
@property (nonatomic ,strong) NSArray<NSString *> *titleArray;
@end

@implementation QueueTaskViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdentifer];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Table view data source

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.titleArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifer forIndexPath:indexPath];
    cell.textLabel.text = self.titleArray[indexPath.row];
    cell.textLabel.numberOfLines = 0;
    return cell;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    
    NSLog(@"-------- 开始处理 --------");

    if (indexPath.row == 0)
    {
        ///异步任务 + 并发队列
        [self asyncTask_cocurrentQueue_Method];
    }
    else if (indexPath.row == 1)
    {
        //异步任务 + 串行队列
        [self asyncTask_serialQueue_Method];
    }
    else if (indexPath.row == 2)
    {
        ///异步任务 + 主队列
        [self asyncTask_mainQueue_Method];
    }
    else if (indexPath.row == 3)
    {
        ///同步任务 + 并发队列
        [self syncTask_cocurrentQueue_Method];
    }
    else if (indexPath.row == 4)
    {
        ///同步任务 + 串行队列
        [self syncTask_serialQueue_Method];
    }
    else if (indexPath.row == 5)
    {
        ///同步任务 + 主队列（上下文是主队列）
        [self syncTask_mainQueue_Method];
    }
    else if (indexPath.row == 6)
    {
        ///同步任务 + 主队列（上下文是一个新建的串行队列）
        [self syncTask_mainQueue_Method2];
    }
    else if (indexPath.row == 7)
    {
        ///同步任务 + 主队列（上下文是一个新建的并发队列）
        [self syncTask_mainQueue_Method3];
    }
    else if (indexPath.row == 8)
    {
        ///串行队列+异步+同步 造成死锁
        [self syncTask_serialQueue_deadLock];
    }
    else if (indexPath.row == 9)
    {
        [self serialQueue_Test];
    }
    else if (indexPath.row == 10)
    {
        [self concurrentQueue_Test];
    }
    else if (indexPath.row == 11)
    {
        [self dispatch_set_target_queue_Method];
    }
    else if (indexPath.row == 12)
    {
        [self dispatch_suspend_Method];
    }
    NSLog(@"-------- 结束处理 --------");

}

#pragma mark - 异步任务

#pragma mark 异步任务 + 并发队列
- (void)asyncTask_cocurrentQueue_Method
{
    //创建一个并发队列
    dispatch_queue_t queue = dispatch_queue_create("com.demo.task0", DISPATCH_QUEUE_CONCURRENT);
    
    //将一个耗时任务A（异步）添加到并发队列 queue 里处理
    dispatch_async(queue, ^{
        NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    });
    NSLog(@"------- 此处为taskA与taskB分界线 -------");
    //追加一个耗时任务B（异步）添加到 并发队列 queue 里处理
    dispatch_async(queue, ^{
        NSLog(@"开始执行：taskB ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskB ------- %@",NSThread.currentThread);
    });
}

#pragma mark 异步任务 + 串行队列
- (void)asyncTask_serialQueue_Method
{
    //创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("com.demo.task1", DISPATCH_QUEUE_SERIAL);
    
    //将一个耗时任务A（异步）添加到串行队列 queue 里处理
    dispatch_async(queue, ^{
        NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    });
    NSLog(@"------- 此处为taskA与taskB分界线 -------");
    //追加一个耗时任务B（异步）添加到 串行队列 queue 里处理
    dispatch_async(queue, ^{
        NSLog(@"开始执行：taskB ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskB ------- %@",NSThread.currentThread);
    });
}

#pragma mark  异步任务 + 主队列
- (void)asyncTask_mainQueue_Method
{
    //获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    //将一个耗时任务A（异步）添加到串行队列 queue 里处理
    dispatch_async(mainQueue, ^{
        NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    });
    NSLog(@"------- 此处为taskA与taskB分界线 -------");
    //追加一个耗时任务B（异步）添加到 串行队列 queue 里处理
    dispatch_async(mainQueue, ^{
        NSLog(@"开始执行：taskB ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskB ------- %@",NSThread.currentThread);
    });
}


#pragma mark - 同步任务
#pragma mark 同步任务 + 并发队列

- (void)syncTask_cocurrentQueue_Method
{
    dispatch_queue_t queue = dispatch_queue_create("com.demo.task4", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    });
    NSLog(@"------- 此处为taskA与taskB分界线 -------");
    dispatch_sync(queue, ^{
        NSLog(@"开始执行：taskB ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskB ------- %@",NSThread.currentThread);
    });
}

#pragma mark 同步任务 + 串行队列

- (void)syncTask_serialQueue_Method
{
    dispatch_queue_t queue = dispatch_queue_create("com.demo.task5", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    });
    NSLog(@"------- 此处为taskA与taskB分界线 -------");
    dispatch_sync(queue, ^{
        NSLog(@"开始执行：taskB ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskB ------- %@",NSThread.currentThread);
    });
}

#pragma mark 同步任务 + 主队列

- (void)syncTask_mainQueue_Method
{
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_sync(mainQueue, ^{
        NSLog(@"开始执行：taskA ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskA ------- %@",NSThread.currentThread);
    });
    NSLog(@"------- 此处为taskA与taskB分界线 -------");
    dispatch_sync(mainQueue, ^{
        NSLog(@"开始执行：taskB ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:3];//模拟耗时任务
        NSLog(@"结束执行：taskB ------- %@",NSThread.currentThread);
    });
}

/*


 */




#pragma mark - 同步执行 的 死锁问题



///串行队列+异步+同步 造成死锁
- (void)syncTask_serialQueue_deadLock
{
    NSLog(@"开始处理 === %@",NSThread.currentThread);
    
    //创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("syncTask_serialQueue_deadLock", DISPATCH_QUEUE_SERIAL);

    //程序报错：Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    //在串行队列队列添加一个并发任务
    dispatch_async(queue, ^{
        NSLog(@"处理耗时任务A == %@",NSThread.currentThread);
        //处理耗时任务A == <NSThread: 0x60400046bb80>{number = 4, name = (null)}
        
        //此处开辟了一条新线程用来处理任务 A
        
        //程序报错： Thread 4: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
        dispatch_sync(queue, ^{
            NSLog(@"同步任务B --- %@",NSThread.currentThread);
        });

    });
    
    NSLog(@"处理完毕 === %@",NSThread.currentThread);
}
/*
 主线程主队列中执行任务 initDispatchQueue,进入了这个方法，在这个方法里面创建了一个串行队列
 给这个串行队列添加了一个异步任务，由于是异步任务，所以会开启一条新的线程，为了方便描述，我们把新开的这个线程记做线程A, 把这个任务记做任务A，也由于是异步任务，主线程就不会等待这个任务返回，就接着往下执行其他任务了。
 接下来的分析就到了这个线程A上，这个任务A被添加到串行队列之后就开始在线程A上执行，打印出了我们的第一条信息，也证明了不是在主线程,这个也没问题。
 线程A开始执行这个任务A，进入这个任务A之后在这个任务A里面又同步在串行队列里面添加任务，记做任务B，由于任务B是dispatch_sync函数同步添加的，需要立马被执行，就等待线程A执行它
 但是这个任务B是添加到串行队列的末尾的，线程A在没有执行完当前任务A是不会去执行它的，这样就造成线程A在等待当前任务A执行完，任务B又在等待线程A执行它，就形成了死锁
 */



- (void)syncTask_mainQueue_Method2
{
    //创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("syncTask_mainQueue_Method2", DISPATCH_QUEUE_SERIAL);
    NSLog(@"开始处理 === %@",NSThread.currentThread);
    
    //获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    //在新创建的串行队列列添加一个异步任务
    dispatch_sync(queue, ^{
        NSLog(@"currentThread === %@",NSThread.currentThread);
        //在主队列添加一个同步任务
        dispatch_sync(mainQueue, ^{
            
            //程序报错：Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
            NSLog(@"处理耗时任务A == %@",NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//模拟耗时任务
        });
        
        //追加一个耗时任务B（同步）添加到 串行队列 queue 里处理
        dispatch_sync(mainQueue, ^{
            NSLog(@"处理耗时任务B == %@",NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//模拟耗时任务
        });
    });
    
    NSLog(@"处理完毕 === %@",NSThread.currentThread);
}

- (void)syncTask_mainQueue_Method3
{
    //创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("syncTask_mainQueue_Method3", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"开始处理 === %@",NSThread.currentThread);
    
    //获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    //在新创建的串行队列列添加一个异步任务
    dispatch_async(queue, ^{
        NSLog(@"currentThread === %@",NSThread.currentThread);
        
        //在主队列添加一个同步任务
        dispatch_sync(mainQueue, ^{
            NSLog(@"处理耗时任务A == %@",NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//模拟耗时任务
        });
        
        //追加一个耗时任务B（同步）添加到 串行队列 queue 里处理
        dispatch_sync(mainQueue, ^{
            NSLog(@"处理耗时任务B == %@",NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//模拟耗时任务
        });
        
    });
    
    NSLog(@"处理完毕 === %@",NSThread.currentThread);
}
/*
 为什么现在就不会异常终止了呢？
 
 因为syncMain 任务放到了其他线程里，而任务A、任务B都在追加到主队列中，这2个任务都会在主线程中执行。
 syncMain 任务在其他线程中执行到追加任务1到主队列中，因为主队列现在没有正在执行的任务，所以，会直接执行主队列的任务1，等任务1执行完毕，再接着执行任务2、任务3。所以这里不会卡住线程。
 */

#pragma mark - 串行队列 与 并行队列 性能测试

- (void)serialQueue_Test
{
    //多个 串行队列之间并行执行
    for (int i = 0; i < 10000; i ++)
    {
        dispatch_queue_t queue = dispatch_queue_create("串行", DISPATCH_QUEUE_SERIAL);
        dispatch_async(queue, ^{
            NSLog(@"第 %d 个串行队列 ======= %@",i,NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//模拟耗时任务
        });
    }
}

- (void)concurrentQueue_Test
{
    for (int i = 0; i < 10000; i ++)
    {
        dispatch_queue_t queue = dispatch_queue_create("并行", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            NSLog(@"第 %d 个并行队列 ======= %@",i,NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//模拟耗时任务
        });
    }
}

#pragma mark - dispatch_set_target_queue

/*
 把一个任务放到一个串行的queue中，如果这个任务被拆分了，被放置到多个串行的queue中，
 但实际还是需要这个任务同步执行，那么就会有问题，因为多个串行queue之间是并行的。
 可以使用dispatch_set_target_queue
 将多个串行的queue使用dispatch_set_target_queue指定到了同一目标，
 那么着多个串行queue在目标queue上就是同步执行的，不再是并行执行
 */
- (void)dispatch_set_target_queue_Method
{
    NSLog(@"开始执行 ===== %@",NSThread.currentThread);
    //dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_queue_t targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);
    
    for (int i = 0; i < 1000; i ++)
    {
        dispatch_queue_t queue = dispatch_queue_create("串行--目标", DISPATCH_QUEUE_SERIAL);
        dispatch_set_target_queue(queue, targetQueue);
        dispatch_async(queue, ^{
            NSLog(@"第 %d 个串行队列 ======= %@",i,NSThread.currentThread);
            [NSThread sleepForTimeInterval:10];//模拟耗时任务
        });
    }
    
    NSLog(@"结束执行 ===== %@",NSThread.currentThread);
}

/*
 //暂停、恢复队列上的任务
 //dispatch_suspend并不会立即暂停正在运行的block，而是在当前block执行完成后，暂停后续的block执行。
 void dispatch_suspend(dispatch_object_t object);
 void dispatch_resume(dispatch_object_t object);
 
 */
- (void)dispatch_suspend_Method
{
    NSLog(@"开始执行 ===== %@",NSThread.currentThread);

    dispatch_queue_t queue = dispatch_queue_create("com.demo.task", DISPATCH_QUEUE_SERIAL);
    
    
    dispatch_async(queue, ^{
        NSLog(@"耗时任务A ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
        NSLog(@"耗时任务A ------- %@",NSThread.currentThread);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"耗时任务B ======= %@",NSThread.currentThread);
        [NSThread sleepForTimeInterval:10];//模拟耗时任务
        NSLog(@"耗时任务B ------- %@",NSThread.currentThread);
    });
    
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        dispatch_suspend(queue);
    });
    
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        dispatch_resume(queue);
    });
    
    NSLog(@"结束执行 ===== %@",NSThread.currentThread);
    
    
    /*
     14:13:58.528028+0800  开始执行 ===== <NSThread: 0x600000072fc0>{number = 1, name = main}
     14:13:58.528685+0800  结束执行 ===== <NSThread: 0x600000072fc0>{number = 1, name = main}
     14:13:58.528804+0800  耗时任务A ======= <NSThread: 0x60400046d700>{number = 3, name = (null)}
     14:14:08.532625+0800  耗时任务A ------- <NSThread: 0x60400046d700>{number = 3, name = (null)}
     14:14:18.529455+0800  耗时任务B ======= <NSThread: 0x600000462580>{number = 4, name = (null)}
     14:14:28.533770+0800  耗时任务B ------- <NSThread: 0x600000462580>{number = 4, name = (null)}
     */
    //任务A在开始10s后结束执行；任务B 没有在 任务A 结束后立即执行，而是在“开始执行”20s秒才开始执行
    //这表明 dispatch_suspend 并不会立即暂停正在运行的block，而是在当前block执行完成后，暂停后续的block执行。
}


#pragma mark - setter and getter

- (NSArray<NSString *> *)titleArray
{
    if (_titleArray == nil)
    {
        _titleArray = @[@"异步任务 + 并发队列",@"异步任务 + 串行队列",@"异步任务 + 主队列",
                        @"同步任务 + 并发队列",@"同步任务 + 串行队列",
                        @"同步任务 + 主队列（上下文在主队列）",
                        @"同步任务 + 主队列（上下文在一个串行队列）",
                        @"同步任务 + 主队列（上下文在一个并发队列）",
                        @"串行队列+异步+同步 造成死锁",
                        @"串行队列性能测试", @"并行队列性能测试",
                        @"dispatch_set_target_queue",
                        @"dispatch_suspend"];
    }
    return _titleArray;
}

@end
